#include <windows.h>
#include <stdio.h>
#include "image.h"
#include "data.h"
#include "drawing.h"
#include "tools.h"
#include "resource.h"

#define NRO_TOOLS 4

LRESULT toolPaintWithBorder(HWND hwnd, PDATA data, UINT message, PVOID param);

LRESULT toolPaintNormal(HWND hwnd, PDATA data, UINT message, PVOID param);

LRESULT toolLineDrawer(HWND hwnd, PDATA data, UINT message, PVOID param);

LRESULT toolBrush(HWND hwnd, PDATA data, UINT message, PVOID param);

TOOL_CALLBACK toolCallback[]={
		toolPaintWithBorder,
		toolPaintNormal,
		toolLineDrawer,
		toolBrush
		};

typedef struct _CURSORMASK_ {
	int dx, dy;
	BYTE bit[8];
	HCURSOR cursor;
} *PCURSORMASK, CURSORMASK;

CURSORMASK cursorMask[1] = {
	{2, 2, {0x70,0xF8,0xF8,0xF8,0x70,0,0,0}, NULL}
	};

// ---------------------- Generic

void toolsPaintAtXY(PDATA data, int x, int y, BOOL fillBorderFirst){
	
	COLORREF current = dataGetColor(data, x, y);
	if (current==0xFFFFFFFF || current==data->colorInk){
		return;
	}

	HBRUSH borderBrush = CreateSolidBrush(data->colorBorder);
	HBRUSH colorBrush = CreateSolidBrush(data->colorPainter);
	HBRUSH oldBrush;
	OutputDebugString("Implement UNDO queue");
	HDC pdc = CreateCompatibleDC(NULL);
	SelectObject(pdc, data->image->hBitmap);
	if (fillBorderFirst){
		oldBrush = (HBRUSH)SelectObject(pdc, borderBrush);
		ExtFloodFill(pdc, x, y, current, FLOODFILLSURFACE);
		SelectObject(pdc, colorBrush);
		ExtFloodFill(pdc, x, y, data->colorBorder, FLOODFILLSURFACE);
	}
	else{
		oldBrush = (HBRUSH)SelectObject(pdc, colorBrush);
		ExtFloodFill(pdc, x, y, current, FLOODFILLSURFACE);
	}
	
	if (oldBrush){
		SelectObject(pdc, oldBrush);
	}
	DeleteObject(borderBrush);
	DeleteObject(colorBrush);
	DeleteDC(pdc);
	data->changed = TRUE;
}

// ---------------------- Tool Callbacks


// A callback allow call different events, like properties definition/configuration, lbuttondown or another

LRESULT toolPaintWithBorder(HWND hwnd, PDATA data, UINT message, PVOID param){
	PMOUSEEVENT mouseEvent = (PMOUSEEVENT)param;
	switch(message){
	case WM_LBUTTONDOWN:
		toolsPaintAtXY(data, mouseEvent->x-data->px, mouseEvent->y-data->py, TRUE);
		return TRUE;
	}	
	return 0;
}

LRESULT toolPaintNormal(HWND hwnd, PDATA data, UINT message, PVOID param){
	PMOUSEEVENT mouseEvent = (PMOUSEEVENT)param;
	switch(message){
	case WM_LBUTTONDOWN:
		toolsPaintAtXY(data, mouseEvent->x-data->px, mouseEvent->y-data->py, FALSE);
		return TRUE;
	}
	return 0;
}

void toolLinePlotBorderPixel(PDATA data, int x, int y){
	COLORREF color = dataGetColor(data, x, y);
	if (color==0xFFFFFFFF || color==data->colorInk){
		return;
	}
	dataSetColor(data, data->colorBorder);
}

LRESULT toolLineDrawer(HWND hwnd, PDATA data, UINT message, PVOID param){
	PMOUSEEVENT mouseEvent = (PMOUSEEVENT)param;
	static int x0, y0, x1, y1;
	static int down = FALSE;
	HDC pdc;
	switch(message){
	case WM_LBUTTONDOWN:
		x0 = mouseEvent->x;
		y0 = mouseEvent->y;
		down = TRUE;
		return TRUE;
	case WM_LBUTTONUP:
		if (down){
			drawingFollowLine(data, x0-data->px, y0-data->py, x1-data->px, y1-data->py, toolLinePlotBorderPixel);
			data->changed = TRUE;
		}
		down = FALSE;
		return TRUE;
	case WM_MOUSEMOVE:
		x1 = mouseEvent->x;
		y1 = mouseEvent->y;
		return down;
	case WM_PAINT:
		if (down){
			pdc = (HDC)param;
			MoveToEx(pdc, x0, y0, NULL);
			LineTo(pdc, x1, y1);
		}
		break;
	}
	return 0;
}

void toolBrushApplyMap(PDATA data, int x, int y, PCURSORMASK cursor, COLORREF color){
	x -= cursor->dx;
	y -= cursor->dy;
	BYTE m[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
	COLORREF base;
	int ix, iy;
	for(iy=0;iy<8;iy++){
		for(ix=0;ix<8;ix++){
			if (cursor->bit[iy] & m[ix]){
				base = dataGetColor(data, x+ix, y+iy);
				if (base==data->colorPaper){
					dataSetColor(data, color);
				}
			}
		}
	}
}

LRESULT toolBrush(HWND hwnd, PDATA data, UINT message, PVOID param){
	PMOUSEEVENT mouseEvent = (PMOUSEEVENT)param;
	static int currentCursor = 0;
	static int down = FALSE;
	static COLORREF color;
	switch(message){
	case TOOL_INIRESOURCES:
		cursorMask[0].cursor = LoadCursor((HINSTANCE)param, MAKEINTRESOURCE(IDC_PINCEL1));
		break;
	case TOOL_GETFOCUS:
		data->cursor = cursorMask[currentCursor].cursor;
		break;
	case WM_LBUTTONDOWN:
		down = TRUE;
		color = dataGetColor(data, mouseEvent->x, mouseEvent->y);
	case WM_MOUSEMOVE:
		if (down){
			toolBrushApplyMap(data, mouseEvent->x, mouseEvent->y, cursorMask + currentCursor, color);
			InvalidateRect(hwnd, NULL, FALSE);
		}
		break;
	case WM_LBUTTONUP:
		down = FALSE;
		break;
	}
	return 0;
}

void toolLoadConfiguration(FILE *fconfig){
	int i;
	for(i=0;i<NRO_TOOLS;i++){
		toolCallback[i](NULL, NULL, TOOL_LOADCONFIG, fconfig);
	}
}

void toolIniResources(HINSTANCE instance){
	int i;
	for(i=0;i<NRO_TOOLS;i++){
		toolCallback[i](NULL, NULL, TOOL_INIRESOURCES, instance);
	}
}

void toolSaveConfiguration(FILE *fconfig){
	int i;
	for(i=0;i<NRO_TOOLS;i++){
		toolCallback[i](NULL, NULL, TOOL_SAVECONFIG, fconfig);
	}
}